/**
 * Popover View
 *
 * Copyright 2012 Daniel Lupia�ez Casares <lupidan@gmail.com>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/

package com.smartandroid.sa.view;

import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class PopoverView extends RelativeLayout implements OnTouchListener {

	// ********************************************************************
	// INTERFACES
	// ********************************************************************
	/**
	 * Interface to get information from the popover view. Use setDelegate to
	 * have access to this methods
	 */
	public static interface PopoverViewDelegate {
		/**
		 * Called when the popover is going to show
		 * 
		 * @param view
		 *            The whole popover view
		 */
		void popoverViewWillShow(PopoverView view);

		/**
		 * Called when the popover did show
		 * 
		 * @param view
		 *            The whole popover view
		 */
		void popoverViewDidShow(PopoverView view);

		/**
		 * Called when the popover is going to be dismissed
		 * 
		 * @param view
		 *            The whole popover view
		 */
		void popoverViewWillDismiss(PopoverView view);

		/**
		 * Called when the popover was dismissed
		 * 
		 * @param view
		 *            The whole popover view
		 */
		void popoverViewDidDismiss(PopoverView view);
	}

	private Context c;

	// ********************************************************************
	// STATIC MEMBERS
	// ********************************************************************
	/**
	 * Popover arrow points up. Integer to use with bit operators to tell the
	 * popover where the arrow should appear and from where the popover should
	 * appear
	 */
	public final static int PopoverArrowDirectionUp = 0x00000001;
	/**
	 * Popover arrow points down. Integer to use with bit operators to tell the
	 * popover where the arrow should appear and from where the popover should
	 * appear
	 */
	public final static int PopoverArrowDirectionDown = 0x00000002;
	/**
	 * Popover arrow points left. Integer to use with bit operators to tell the
	 * popover where the arrow should appear and from where the popover should
	 * appear
	 */
	public final static int PopoverArrowDirectionLeft = 0x00000004;
	/**
	 * Popover arrow points right. Integer to use with bit operators to tell the
	 * popover where the arrow should appear and from where the popover should
	 * appear
	 */
	public final static int PopoverArrowDirectionRight = 0x00000008;
	/**
	 * Popover arrow points any direction. Integer to use with bit operators to
	 * tell the popover where the arrow should appear and from where the popover
	 * should appear
	 */
	public final static int PopoverArrowDirectionAny = PopoverArrowDirectionUp
			| PopoverArrowDirectionDown | PopoverArrowDirectionLeft
			| PopoverArrowDirectionRight;
	/**
	 * The default popover background drawable for all the popovers
	 */
	public static int defaultPopoverBackgroundDrawable;// =
														// R.drawable.background_popover;
	/**
	 * The default popover arrow up drawable for all the popovers
	 */
	public static int defaultPopoverArrowUpDrawable;// =
													// R.drawable.icon_popover_arrow_up;
	/**
	 * The default popover arrow down drawable for all the popovers
	 */
	public static int defaultPopoverArrowDownDrawable;// =
														// R.drawable.icon_popover_arrow_down;
	/**
	 * The default popover arrow left drawable for all the popovers
	 */
	public static int defaultPopoverArrowLeftDrawable;// =
														// R.drawable.icon_popover_arrow_left;
	/**
	 * The default popover arrow down drawable for all the popovers
	 */
	public static int defaultPopoverArrowRightDrawable;// =
														// R.drawable.icon_popover_arrow_right;

	// ********************************************************************
	// STATIC METHODS
	// ********************************************************************
	/**
	 * Get the Rect frame for a view (relative to the Window of the application)
	 * 
	 * @param v
	 *            The view to get the rect from
	 * @return The rect of the view, relative to the application window
	 */
	public static Rect getFrameForView(View v) {
		int location[] = new int[2];
		v.getLocationOnScreen(location);
		Rect viewRect = new Rect(location[0], location[1], location[0]
				+ v.getWidth(), location[1] + v.getHeight());
		return viewRect;
	}

	// ********************************************************************
	// MEMBERS
	// ********************************************************************
	/**
	 * The delegate of the view
	 */
	private PopoverViewDelegate delegate;
	/**
	 * The main popover containing the view we want to show
	 */
	private RelativeLayout popoverView;
	/**
	 * The view group storing this popover. We need this so, when we dismiss the
	 * popover, we remove it from the view group
	 */
	private ViewGroup superview;
	/**
	 * The content size for the view in the popover
	 */
	private Point contentSizeForViewInPopover = new Point(0, 0);
	/**
	 * The real content size we will use (it considers the padding)
	 */
	private Point realContentSize = new Point(0, 0);
	/**
	 * A hash containing
	 */
	private Map<Integer, Rect> possibleRects;
	/**
	 * Whether the view is animating or not
	 */
	private boolean isAnimating = false;
	/**
	 * The fade animation time in milliseconds
	 */
	private int fadeAnimationTime = 300;
	/**
	 * The layout Rect, is the same as the superview rect
	 */
	private Rect popoverLayoutRect;
	/**
	 * The popover background drawable
	 */
	private int popoverBackgroundDrawable;
	/**
	 * The popover arrow up drawable
	 */
	private int popoverArrowUpDrawable;
	/**
	 * The popover arrow down drawable
	 */
	private int popoverArrowDownDrawable;
	/**
	 * The popover arrow left drawable
	 */
	private int popoverArrowLeftDrawable;
	/**
	 * The popover arrow down drawable
	 */
	private int popoverArrowRightDrawable;

	// ********************************************************************
	// CONSTRUCTORS
	// ********************************************************************
	/**
	 * Constructor to create a popover with a popover view
	 * 
	 * @param context
	 *            The context where we should create the popover view
	 * @param layoutId
	 *            The ID of the layout we want to put inside the popover
	 */
	public PopoverView(Context context, int layoutId) {
		super(context);
		this.c = context;

		defaultPopoverBackgroundDrawable = getResources().getIdentifier(
				"background_popover", "drawable", context.getPackageName());
		// defaultPopoverBackgroundDrawable = R.drawable.background_popover;
		/**
		 * The default popover arrow up drawable for all the popovers
		 */
		defaultPopoverArrowUpDrawable = getResources().getIdentifier(
				"icon_popover_arrow_up", "drawable", context.getPackageName());
		// defaultPopoverArrowUpDrawable = R.drawable.icon_popover_arrow_up;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowDownDrawable = getResources()
				.getIdentifier("icon_popover_arrow_down", "drawable",
						context.getPackageName());
		// defaultPopoverArrowDownDrawable = R.drawable.icon_popover_arrow_down;
		/**
		 * The default popover arrow left drawable for all the popovers
		 */
		defaultPopoverArrowLeftDrawable = getResources()
				.getIdentifier("icon_popover_arrow_left", "drawable",
						context.getPackageName());
		// defaultPopoverArrowLeftDrawable = R.drawable.icon_popover_arrow_left;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowRightDrawable = getResources().getIdentifier(
				"icon_popover_arrow_right", "drawable",
				context.getPackageName());
		// defaultPopoverArrowRightDrawable =
		// R.drawable.icon_popover_arrow_right;

		initPopoverView(inflate(context, layoutId, null));
	}

	/**
	 * Constructor to create a popover with a popover view
	 * 
	 * @param context
	 *            The context where we should create the popover view
	 * @param attrs
	 *            Attribute set to init the view
	 * @param layoutId
	 *            The ID of the layout we want to put inside the popover
	 */
	public PopoverView(Context context, AttributeSet attrs, int layoutId) {
		super(context, attrs);
		this.c = context;
		defaultPopoverBackgroundDrawable = getResources().getIdentifier(
				"background_popover", "drawable", context.getPackageName());
		// defaultPopoverBackgroundDrawable = R.drawable.background_popover;
		/**
		 * The default popover arrow up drawable for all the popovers
		 */
		defaultPopoverArrowUpDrawable = getResources().getIdentifier(
				"icon_popover_arrow_up", "drawable", context.getPackageName());
		// defaultPopoverArrowUpDrawable = R.drawable.icon_popover_arrow_up;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowDownDrawable = getResources()
				.getIdentifier("icon_popover_arrow_down", "drawable",
						context.getPackageName());
		// defaultPopoverArrowDownDrawable = R.drawable.icon_popover_arrow_down;
		/**
		 * The default popover arrow left drawable for all the popovers
		 */
		defaultPopoverArrowLeftDrawable = getResources()
				.getIdentifier("icon_popover_arrow_left", "drawable",
						context.getPackageName());
		// defaultPopoverArrowLeftDrawable = R.drawable.icon_popover_arrow_left;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowRightDrawable = getResources().getIdentifier(
				"icon_popover_arrow_right", "drawable",
				context.getPackageName());
		// defaultPopoverArrowRightDrawable =
		// R.drawable.icon_popover_arrow_right;
		initPopoverView(inflate(context, layoutId, null));
	}

	/**
	 * Constructor to create a popover with a popover view
	 * 
	 * @param context
	 *            The context where we should create the popover view
	 * @param attrs
	 *            Attribute set to init the view
	 * @param defStyle
	 *            The default style for this view
	 * @param layoutId
	 *            The ID of the layout we want to put inside the popover
	 */
	public PopoverView(Context context, AttributeSet attrs, int defStyle,
			int layoutId) {

		super(context, attrs, defStyle);
		this.c = context;
		defaultPopoverBackgroundDrawable = getResources().getIdentifier(
				"background_popover", "drawable", context.getPackageName());
		// defaultPopoverBackgroundDrawable = R.drawable.background_popover;
		/**
		 * The default popover arrow up drawable for all the popovers
		 */
		defaultPopoverArrowUpDrawable = getResources().getIdentifier(
				"icon_popover_arrow_up", "drawable", context.getPackageName());
		// defaultPopoverArrowUpDrawable = R.drawable.icon_popover_arrow_up;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowDownDrawable = getResources()
				.getIdentifier("icon_popover_arrow_down", "drawable",
						context.getPackageName());
		// defaultPopoverArrowDownDrawable = R.drawable.icon_popover_arrow_down;
		/**
		 * The default popover arrow left drawable for all the popovers
		 */
		defaultPopoverArrowLeftDrawable = getResources()
				.getIdentifier("icon_popover_arrow_left", "drawable",
						context.getPackageName());
		// defaultPopoverArrowLeftDrawable = R.drawable.icon_popover_arrow_left;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowRightDrawable = getResources().getIdentifier(
				"icon_popover_arrow_right", "drawable",
				context.getPackageName());
		// defaultPopoverArrowRightDrawable =
		// R.drawable.icon_popover_arrow_right;
		initPopoverView(inflate(context, layoutId, null));
	}

	/**
	 * Constructor to create a popover with a popover view
	 * 
	 * @param context
	 *            The context where we should create the popover view
	 * @param popoverView
	 *            The inner view we want to show in a popover
	 */
	public PopoverView(Context context, View popoverView) {
		super(context);
		this.c = context;
		defaultPopoverBackgroundDrawable = getResources().getIdentifier(
				"background_popover", "drawable", context.getPackageName());
		// defaultPopoverBackgroundDrawable = R.drawable.background_popover;
		/**
		 * The default popover arrow up drawable for all the popovers
		 */
		defaultPopoverArrowUpDrawable = getResources().getIdentifier(
				"icon_popover_arrow_up", "drawable", context.getPackageName());
		// defaultPopoverArrowUpDrawable = R.drawable.icon_popover_arrow_up;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowDownDrawable = getResources()
				.getIdentifier("icon_popover_arrow_down", "drawable",
						context.getPackageName());
		// defaultPopoverArrowDownDrawable = R.drawable.icon_popover_arrow_down;
		/**
		 * The default popover arrow left drawable for all the popovers
		 */
		defaultPopoverArrowLeftDrawable = getResources()
				.getIdentifier("icon_popover_arrow_left", "drawable",
						context.getPackageName());
		// defaultPopoverArrowLeftDrawable = R.drawable.icon_popover_arrow_left;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowRightDrawable = getResources().getIdentifier(
				"icon_popover_arrow_right", "drawable",
				context.getPackageName());
		// defaultPopoverArrowRightDrawable =
		// R.drawable.icon_popover_arrow_right;
		initPopoverView(popoverView);
	}

	/**
	 * Constructor to create a popover with a popover view
	 * 
	 * @param context
	 *            The context where we should create the popover view
	 * @param attrs
	 *            Attribute set to init the view
	 * @param popoverView
	 *            The inner view we want to show in a popover
	 */
	public PopoverView(Context context, AttributeSet attrs, View popoverView) {
		super(context, attrs);
		this.c = context;
		defaultPopoverBackgroundDrawable = getResources().getIdentifier(
				"background_popover", "drawable", context.getPackageName());
		// defaultPopoverBackgroundDrawable = R.drawable.background_popover;
		/**
		 * The default popover arrow up drawable for all the popovers
		 */
		defaultPopoverArrowUpDrawable = getResources().getIdentifier(
				"icon_popover_arrow_up", "drawable", context.getPackageName());
		// defaultPopoverArrowUpDrawable = R.drawable.icon_popover_arrow_up;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowDownDrawable = getResources()
				.getIdentifier("icon_popover_arrow_down", "drawable",
						context.getPackageName());
		// defaultPopoverArrowDownDrawable = R.drawable.icon_popover_arrow_down;
		/**
		 * The default popover arrow left drawable for all the popovers
		 */
		defaultPopoverArrowLeftDrawable = getResources()
				.getIdentifier("icon_popover_arrow_left", "drawable",
						context.getPackageName());
		// defaultPopoverArrowLeftDrawable = R.drawable.icon_popover_arrow_left;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowRightDrawable = getResources().getIdentifier(
				"icon_popover_arrow_right", "drawable",
				context.getPackageName());
		// defaultPopoverArrowRightDrawable =
		// R.drawable.icon_popover_arrow_right;
		initPopoverView(popoverView);
	}

	/**
	 * Constructor to create a popover with a popover view
	 * 
	 * @param context
	 *            The context where we should create the popover view
	 * @param attrs
	 *            Attribute set to init the view
	 * @param defStyle
	 *            The default style for this view
	 * @param popoverView
	 *            The inner view we want to show in a popover
	 */
	public PopoverView(Context context, AttributeSet attrs, int defStyle,
			View popoverView) {
		super(context, attrs, defStyle);
		this.c = context;
		defaultPopoverBackgroundDrawable = getResources().getIdentifier(
				"background_popover", "drawable", context.getPackageName());
		// defaultPopoverBackgroundDrawable = R.drawable.background_popover;
		/**
		 * The default popover arrow up drawable for all the popovers
		 */
		defaultPopoverArrowUpDrawable = getResources().getIdentifier(
				"icon_popover_arrow_up", "drawable", context.getPackageName());
		// defaultPopoverArrowUpDrawable = R.drawable.icon_popover_arrow_up;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowDownDrawable = getResources()
				.getIdentifier("icon_popover_arrow_down", "drawable",
						context.getPackageName());
		// defaultPopoverArrowDownDrawable = R.drawable.icon_popover_arrow_down;
		/**
		 * The default popover arrow left drawable for all the popovers
		 */
		defaultPopoverArrowLeftDrawable = getResources()
				.getIdentifier("icon_popover_arrow_left", "drawable",
						context.getPackageName());
		// defaultPopoverArrowLeftDrawable = R.drawable.icon_popover_arrow_left;
		/**
		 * The default popover arrow down drawable for all the popovers
		 */
		defaultPopoverArrowRightDrawable = getResources().getIdentifier(
				"icon_popover_arrow_right", "drawable",
				context.getPackageName());
		// defaultPopoverArrowRightDrawable =
		// R.drawable.icon_popover_arrow_right;
		initPopoverView(popoverView);
	}

	/**
	 * Init the popover view
	 * 
	 * @param viewToEnclose
	 *            The view we wan to insert inside the popover
	 */
	private void initPopoverView(View viewToEnclose) {

		// Configure self
		setBackgroundColor(0x00000000);
		// setOnClickListener(this);
		setOnTouchListener(this);

		// Set initial drawables
		popoverBackgroundDrawable = PopoverView.defaultPopoverBackgroundDrawable;
		popoverArrowUpDrawable = PopoverView.defaultPopoverArrowUpDrawable;
		popoverArrowDownDrawable = PopoverView.defaultPopoverArrowDownDrawable;
		popoverArrowLeftDrawable = PopoverView.defaultPopoverArrowLeftDrawable;
		popoverArrowRightDrawable = PopoverView.defaultPopoverArrowRightDrawable;

		// Init the relative layout
		popoverView = new RelativeLayout(getContext());
		popoverView.setBackgroundDrawable(getResources().getDrawable(
				popoverBackgroundDrawable));
		popoverView.addView(viewToEnclose, LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT);

	}

	// ********************************************************************
	// PRIVATE METHODS
	// ********************************************************************
	/**
	 * Add the popover to the view with a defined rect inside the popover
	 * 
	 * @param insertRect
	 *            The rect we want to insert the view
	 */
	private void addPopoverInRect(Rect insertRect) {
		// Set layout params
		LayoutParams insertParams = new LayoutParams(insertRect.width(),
				insertRect.height());
		insertParams.leftMargin = insertRect.left;
		insertParams.topMargin = insertRect.top;
		// Add the view
		addView(popoverView, insertParams);

	}

	private void addArrow(Rect originRect, Integer arrowDirection) {
		// Add arrow drawable
		ImageView arrowImageView = new ImageView(getContext());
		Drawable arrowDrawable = null;
		int xPos = 0;
		int arrowWidth = 0;
		int yPos = 0;
		int arrowHeight = 0;
		// Get correct drawable, and get Width, Height, Xpos and yPos depending
		// on the selected arrow direction
		if (arrowDirection == PopoverView.PopoverArrowDirectionUp) {
			arrowDrawable = getResources().getDrawable(popoverArrowUpDrawable);
			arrowWidth = arrowDrawable.getIntrinsicWidth();
			arrowHeight = arrowDrawable.getIntrinsicHeight();
			xPos = originRect.centerX() - (arrowWidth / 2)
					- popoverLayoutRect.left;
			yPos = originRect.bottom - popoverLayoutRect.top;
		} else if (arrowDirection == PopoverView.PopoverArrowDirectionDown) {
			arrowDrawable = getResources()
					.getDrawable(popoverArrowDownDrawable);
			arrowWidth = arrowDrawable.getIntrinsicWidth();
			arrowHeight = arrowDrawable.getIntrinsicHeight();
			xPos = originRect.centerX() - (arrowWidth / 2)
					- popoverLayoutRect.left;
			yPos = originRect.top - arrowHeight - popoverLayoutRect.top;
		} else if (arrowDirection == PopoverView.PopoverArrowDirectionLeft) {
			arrowDrawable = getResources()
					.getDrawable(popoverArrowLeftDrawable);
			arrowWidth = arrowDrawable.getIntrinsicWidth();
			arrowHeight = arrowDrawable.getIntrinsicHeight();
			xPos = originRect.right - popoverLayoutRect.left;
			yPos = originRect.centerY() - (arrowHeight / 2)
					- popoverLayoutRect.top;
		} else if (arrowDirection == PopoverView.PopoverArrowDirectionRight) {
			arrowDrawable = getResources().getDrawable(
					popoverArrowRightDrawable);
			arrowWidth = arrowDrawable.getIntrinsicWidth();
			arrowHeight = arrowDrawable.getIntrinsicHeight();
			xPos = originRect.left - arrowWidth - popoverLayoutRect.left;
			yPos = originRect.centerY() - (arrowHeight / 2)
					- popoverLayoutRect.top;
		}
		// Set drawable
		arrowImageView.setImageDrawable(arrowDrawable);
		// Init layout params
		LayoutParams arrowParams = new LayoutParams(arrowWidth, arrowHeight);
		arrowParams.leftMargin = xPos;
		arrowParams.topMargin = yPos;
		// add view :)
		addView(arrowImageView, arrowParams);
	}

	/**
	 * Calculates the rect for showing the view with Arrow Up
	 * 
	 * @param originRect
	 *            The origin rect
	 * @return The calculated rect to show the view
	 */
	private Rect getRectForArrowUp(Rect originRect) {

		// Get available space
		int xAvailable = popoverLayoutRect.width();
		if (xAvailable < 0)
			xAvailable = 0;
		int yAvailable = popoverLayoutRect.height()
				- (originRect.bottom - popoverLayoutRect.top);
		if (yAvailable < 0)
			yAvailable = 0;

		// Get final width and height
		int finalX = xAvailable;
		if ((realContentSize.x > 0) && (realContentSize.x < finalX))
			finalX = realContentSize.x;
		int finalY = yAvailable;
		if ((realContentSize.y > 0) && (realContentSize.y < finalY))
			finalY = realContentSize.y;

		// Get final origin X and Y
		int originX = (originRect.centerX() - popoverLayoutRect.left)
				- (finalX / 2);
		if (originX < 0)
			originX = 0;
		else if (originX + finalX > popoverLayoutRect.width())
			originX = popoverLayoutRect.width() - finalX;
		int originY = (originRect.bottom - popoverLayoutRect.top);

		// Create rect
		Rect finalRect = new Rect(originX, originY, originX + finalX, originY
				+ finalY);
		// And return
		return finalRect;

	}

	/**
	 * Calculates the rect for showing the view with Arrow Down
	 * 
	 * @param originRect
	 *            The origin rect
	 * @return The calculated rect to show the view
	 */
	private Rect getRectForArrowDown(Rect originRect) {

		// Get available space
		int xAvailable = popoverLayoutRect.width();
		if (xAvailable < 0)
			xAvailable = 0;
		int yAvailable = (originRect.top - popoverLayoutRect.top);
		if (yAvailable < 0)
			yAvailable = 0;

		// Get final width and height
		int finalX = xAvailable;
		if ((realContentSize.x > 0) && (realContentSize.x < finalX))
			finalX = realContentSize.x;
		int finalY = yAvailable;
		if ((realContentSize.y > 0) && (realContentSize.y < finalY))
			finalY = realContentSize.y;

		// Get final origin X and Y
		int originX = (originRect.centerX() - popoverLayoutRect.left)
				- (finalX / 2);
		if (originX < 0)
			originX = 0;
		else if (originX + finalX > popoverLayoutRect.width())
			originX = popoverLayoutRect.width() - finalX;
		int originY = (originRect.top - popoverLayoutRect.top) - finalY;

		// Create rect
		Rect finalRect = new Rect(originX, originY, originX + finalX, originY
				+ finalY);
		// And return
		return finalRect;

	}

	/**
	 * Calculates the rect for showing the view with Arrow Right
	 * 
	 * @param originRect
	 *            The origin rect
	 * @return The calculated rect to show the view
	 */
	private Rect getRectForArrowRight(Rect originRect) {
		// Get available space
		int xAvailable = (originRect.left - popoverLayoutRect.left);
		if (xAvailable < 0)
			xAvailable = 0;
		int yAvailable = popoverLayoutRect.height();
		if (yAvailable < 0)
			yAvailable = 0;

		// Get final width and height
		int finalX = xAvailable;
		if ((realContentSize.x > 0) && (realContentSize.x < finalX))
			finalX = realContentSize.x;
		int finalY = yAvailable;
		if ((realContentSize.y > 0) && (realContentSize.y < finalY))
			finalY = realContentSize.y;

		// Get final origin X and Y
		int originX = (originRect.left - popoverLayoutRect.left) - finalX;
		int originY = (originRect.centerY() - popoverLayoutRect.top)
				- (finalY / 2);
		if (originY < 0)
			originY = 0;
		else if (originY + finalY > popoverLayoutRect.height())
			originY = popoverLayoutRect.height() - finalY;

		// Create rect
		Rect finalRect = new Rect(originX, originY, originX + finalX, originY
				+ finalY);
		// And return
		return finalRect;
	}

	/**
	 * Calculates the rect for showing the view with Arrow Left
	 * 
	 * @param originRect
	 *            The origin rect
	 * @return The calculated rect to show the view
	 */
	private Rect getRectForArrowLeft(Rect originRect) {
		// Get available space
		int xAvailable = popoverLayoutRect.width()
				- (originRect.right - popoverLayoutRect.left);
		if (xAvailable < 0)
			xAvailable = 0;
		int yAvailable = popoverLayoutRect.height();
		if (yAvailable < 0)
			yAvailable = 0;

		// Get final width and height
		int finalX = xAvailable;
		if ((realContentSize.x > 0) && (realContentSize.x < finalX))
			finalX = realContentSize.x;
		int finalY = yAvailable;
		if ((realContentSize.y > 0) && (realContentSize.y < finalY))
			finalY = realContentSize.y;

		// Get final origin X and Y
		int originX = (originRect.right - popoverLayoutRect.left);
		int originY = (originRect.centerY() - popoverLayoutRect.top)
				- (finalY / 2);
		if (originY < 0)
			originY = 0;
		else if (originY + finalY > popoverLayoutRect.height())
			originY = popoverLayoutRect.height() - finalY;

		// Create rect
		Rect finalRect = new Rect(originX, originY, originX + finalX, originY
				+ finalY);
		// And return
		return finalRect;
	}

	/**
	 * Add available rects for each selected arrow direction
	 * 
	 * @param originRect
	 *            The rect where the popover will appear from
	 * @param arrowDirections
	 *            The bit mask for the possible arrow directions
	 */
	private void addAvailableRects(Rect originRect, int arrowDirections) {
		// Get popover rects for the available directions
		possibleRects = new HashMap<Integer, Rect>();
		if ((arrowDirections & PopoverView.PopoverArrowDirectionUp) != 0) {
			possibleRects.put(PopoverView.PopoverArrowDirectionUp,
					getRectForArrowUp(originRect));
		}
		if ((arrowDirections & PopoverView.PopoverArrowDirectionDown) != 0) {
			possibleRects.put(PopoverView.PopoverArrowDirectionDown,
					getRectForArrowDown(originRect));
		}
		if ((arrowDirections & PopoverView.PopoverArrowDirectionRight) != 0) {
			possibleRects.put(PopoverView.PopoverArrowDirectionRight,
					getRectForArrowRight(originRect));
		}
		if ((arrowDirections & PopoverView.PopoverArrowDirectionLeft) != 0) {
			possibleRects.put(PopoverView.PopoverArrowDirectionLeft,
					getRectForArrowLeft(originRect));
		}

	}

	/**
	 * Get the best available rect (bigger area)
	 * 
	 * @return The Integer key to get the Rect from posibleRects
	 *         (PopoverArrowDirectionUp
	 *         ,PopoverArrowDirectionDown,PopoverArrowDirectionRight or
	 *         PopoverArrowDirectionLeft)
	 */
	private Integer getBestRect() {
		// Get the best one (bigger area)
		Integer best = null;
		for (Integer arrowDir : possibleRects.keySet()) {
			if (best == null) {
				best = arrowDir;
			} else {
				Rect bestRect = possibleRects.get(best);
				Rect checkRect = possibleRects.get(arrowDir);
				if ((bestRect.width() * bestRect.height()) < (checkRect.width() * checkRect
						.height()))
					best = arrowDir;
			}
		}
		return best;
	}

	// ********************************************************************
	// GETTERS AND SETTERS
	// ********************************************************************
	/**
	 * Gets the current fade animation time
	 * 
	 * @return The fade animation time, in milliseconds
	 */
	public int getFadeAnimationTime() {
		return fadeAnimationTime;
	}

	/**
	 * Sets the fade animation time
	 * 
	 * @param fadeAnimationTime
	 *            The time in milliseconds
	 */
	public void setFadeAnimationTime(int fadeAnimationTime) {
		this.fadeAnimationTime = fadeAnimationTime;
	}

	/**
	 * Get the content size for view in popover
	 * 
	 * @return The point with the content size
	 */
	public Point getContentSizeForViewInPopover() {
		return contentSizeForViewInPopover;
	}

	/**
	 * Sets the content size for the view in a popover, if point is (0,0) the
	 * popover will full the screen
	 * 
	 * @param contentSizeForViewInPopover
	 */
	public void setContentSizeForViewInPopover(Point contentSizeForViewInPopover) {
		this.contentSizeForViewInPopover = contentSizeForViewInPopover;
		// Save the real content size
		realContentSize = new Point(contentSizeForViewInPopover);
		realContentSize.x += popoverView.getPaddingLeft()
				+ popoverView.getPaddingRight();
		realContentSize.y += popoverView.getPaddingTop()
				+ popoverView.getPaddingBottom();

	}

	/**
	 * Gets the current delegate
	 * 
	 * @return The current delegate
	 */
	public PopoverViewDelegate getDelegate() {
		return delegate;
	}

	/**
	 * Sets the popover delegate
	 * 
	 * @param delegate
	 *            The new popover delegate
	 */
	public void setDelegate(PopoverViewDelegate delegate) {
		this.delegate = delegate;
	}

	/**
	 * @return Current background drawable
	 */
	public int getPopoverBackgroundDrawable() {
		return popoverBackgroundDrawable;
	}

	/**
	 * @param popoverBackgroundDrawable
	 *            The new background drawable
	 */
	public void setPopoverBackgroundDrawable(int popoverBackgroundDrawable) {
		this.popoverBackgroundDrawable = popoverBackgroundDrawable;
	}

	/**
	 * @return Current arrow up drawable
	 */
	public int getPopoverArrowUpDrawable() {
		return popoverArrowUpDrawable;
	}

	/**
	 * @param popoverArrowUpDrawable
	 *            The new arrow up drawable
	 */
	public void setPopoverArrowUpDrawable(int popoverArrowUpDrawable) {
		this.popoverArrowUpDrawable = popoverArrowUpDrawable;
	}

	/**
	 * @return Current arrow down drawable
	 */
	public int getPopoverArrowDownDrawable() {
		return popoverArrowDownDrawable;
	}

	/**
	 * @param popoverArrowDownDrawable
	 *            The new arrow down drawable
	 */
	public void setPopoverArrowDownDrawable(int popoverArrowDownDrawable) {
		this.popoverArrowDownDrawable = popoverArrowDownDrawable;
	}

	/**
	 * @return Current arrow left drawable
	 */
	public int getPopoverArrowLeftDrawable() {
		return popoverArrowLeftDrawable;
	}

	/**
	 * @param popoverArrowLeftDrawable
	 *            The new arrow left drawable
	 */
	public void setPopoverArrowLeftDrawable(int popoverArrowLeftDrawable) {
		this.popoverArrowLeftDrawable = popoverArrowLeftDrawable;
	}

	/**
	 * @return Current arrow right drawable
	 */
	public int getPopoverArrowRightDrawable() {
		return popoverArrowRightDrawable;
	}

	/**
	 * @param popoverArrowRightDrawable
	 *            The new arrow right drawable
	 */
	public void setPopoverArrowRightDrawable(int popoverArrowRightDrawable) {
		this.popoverArrowRightDrawable = popoverArrowRightDrawable;
	}

	// ********************************************************************
	// PUBLIC METHODS
	// ********************************************************************
	/**
	 * This method shows a popover in a ViewGroup, from an origin rect (relative
	 * to the Application Window)
	 * 
	 * @param group
	 *            The group we want to insert the popup. Normally a Relative
	 *            Layout so it can stand on top of everything
	 * @param originRect
	 *            The rect we want the popup to appear from (relative to the
	 *            Application Window!)
	 * @param arrowDirections
	 *            The mask of bits to tell in which directions we want the
	 *            popover to be shown
	 * @param animated
	 *            Whether is animated, or not
	 */
	public void showPopoverFromRectInViewGroup(ViewGroup group,
			Rect originRect, int arrowDirections, boolean animated) {

		// First, tell delegate we will show
		if (delegate != null)
			delegate.popoverViewWillShow(this);

		// Save superview
		superview = group;

		// First, add the view to the view group. The popover will cover the
		// whole area
		android.view.ViewGroup.LayoutParams insertParams = new android.view.ViewGroup.LayoutParams(
				android.view.ViewGroup.LayoutParams.FILL_PARENT,
				android.view.ViewGroup.LayoutParams.FILL_PARENT);
		group.addView(this, insertParams);

		// Now, save rect for the layout (is the same as the superview)
		popoverLayoutRect = PopoverView.getFrameForView(superview);

		// Add available rects
		addAvailableRects(originRect, arrowDirections);
		// Get best rect
		Integer best = getBestRect();

		// Add popover
		Rect bestRect = possibleRects.get(best);
		addPopoverInRect(bestRect);
		// Add arrow image
		addArrow(originRect, best);

		// If we don't want animation, just tell the delegate
		if (!animated) {
			// Tell delegate we did show
			if (delegate != null)
				delegate.popoverViewDidShow(this);
		}
		// If we want animation, animate it!
		else {
			// Continue only if we are not animating
			if (!isAnimating) {

				// Create alpha animation, with its listener
				AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
				animation.setDuration(fadeAnimationTime);
				animation.setAnimationListener(new AnimationListener() {
					@Override
					public void onAnimationStart(Animation animation) {
						// Nothing to do here
					}

					@Override
					public void onAnimationRepeat(Animation animation) {
						// Nothing to do here
					}

					@Override
					public void onAnimationEnd(Animation animation) {
						// End animation
						isAnimating = false;
						// Tell delegate we did show
						if (delegate != null)
							delegate.popoverViewDidShow(PopoverView.this);
					}
				});

				// Start animation
				isAnimating = true;
				startAnimation(animation);

			}
		}

	}

	/**
	 * Dismiss the current shown popover
	 * 
	 * @param animated
	 *            Whether it should be dismissed animated or not
	 */
	public void dissmissPopover(boolean animated) {

		// Tell delegate we will dismiss
		if (delegate != null)
			delegate.popoverViewWillDismiss(PopoverView.this);

		// If we don't want animation
		if (!animated) {
			// Just remove views
			popoverView.removeAllViews();
			removeAllViews();
			superview.removeView(this);
			// Tell delegate we did dismiss
			if (delegate != null)
				delegate.popoverViewDidDismiss(PopoverView.this);
		} else {
			// Continue only if there is not an animation in progress
			if (!isAnimating) {
				// Create alpha animation, with its listener
				AlphaAnimation animation = new AlphaAnimation(1.0f, 0.0f);
				animation.setDuration(fadeAnimationTime);
				animation.setAnimationListener(new AnimationListener() {
					@Override
					public void onAnimationStart(Animation animation) {
						// Nothing to do here
					}

					@Override
					public void onAnimationRepeat(Animation animation) {
						// Nothing to do here
					}

					@Override
					public void onAnimationEnd(Animation animation) {
						// Remove the view
						popoverView.removeAllViews();
						removeAllViews();
						PopoverView.this.superview.removeView(PopoverView.this);
						// End animation
						isAnimating = false;
						// Tell delegate we did dismiss
						if (delegate != null)
							delegate.popoverViewDidDismiss(PopoverView.this);
					}
				});

				// Start animation
				isAnimating = true;
				startAnimation(animation);
			}

		}

	}

	// ********************************************************************
	// ON TOUCH LISTENER
	// ********************************************************************
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		// If we touched over the background popover view (this)
		if ((!isAnimating) && (v == this)) {
			dissmissPopover(true);
		}
		return true;
	}

}
